var net = require('net');
var Crypto = require('crypto');
/*---------------------extension function*/
function find(fn){
	var arrayItem = this;
	for(var i = 0, len = arrayItem.length; i < len; i++) {
		if(fn(arrayItem[i])){
			return true;
		}
	}
	return false;
}

function each(fn){
	var arrayItem = this;
	for(var i = 0, len = arrayItem.length; i < len; i++) {
		fn(arrayItem[i], i);
	}
}

Array.prototype.find = find;
Array.prototype.each = each;

Object.prototype.find = function(fn){
	for(var name in this) {
		if(fn(this[name], name)){
			return true;
		}
	}
	return false;
};

var t = console.log;
console.log = function(msg) {
    t('['+ msg +"]");
};

function trim(str){
    return str.replace(' ', '');
}

function extend(target, source){
	for(var name in source){
		target[name] = source[name];
	}
}
/*---------------------------------------------------------------*/
/*-----tools--function-----------*/
function pack(num) {
  var result = '';
  result += String.fromCharCode(num >> 24 & 0xFF);
  result += String.fromCharCode(num >> 16 & 0xFF);
  result += String.fromCharCode(num >> 8 & 0xFF);
  result += String.fromCharCode(num & 0xFF);
  return result;
}
/*------------server-------------------*/
var server = net.createServer(function (c) {
	var that = this;
	c.on('data', function(data){
		data = data.toString('binary');
		console.log("req:"+ data);
		var session = this;
		//check the queue
		var exist = that.sessions.find(function(item, name){
			var number = Number(name);
			if(!isNaN(number)){
				return item.guid == session.guid;
			}
		});
		//if the queue contains the session
		if(!exist){
			if(that.is_websocket_req(data)) {
				var headers = that.parse_headers(data);
				if(headers){
					var upgradeHead = that.get_upgradeHead(data);
					var response =that.response_shake(headers, upgradeHead) ;
					session.write(response, 'binary');
					that.push_session(session);
				}
				else {
					//websocket only
					session.destroy();
				}
			}
			else {
				console.log("no_socket_req");
				//websocket only
				session.destroy();
			}
		}
		else {
			console.log("session in");
			//has handshaked
			for(var name in that.sessions) {
				var item = that.sessions[name];
				console.log(item);
				item.write(data);
			}
		}
	});
	c.on('connect', function(){
	});
	c.on('close', function(){
		delete that.sessions[this.guid];
	});	
});
extend(server, {
	guid: 0,
	sessions: new Object,
	push_session: function(session){
		var that = this;
		that.guid++;
		session.guid = that.guid;
		that.sessions[session.guid] = session;
	},
	is_websocket_req: function(req){
		return req.indexOf('\r\n\r\n') !== -1;
	},
	//the second line is the token's source
	get_upgradeHead: function(data){
		var headers = data.split('\r\n\r\n');
		return headers[1];
	},
	//parse the headers'string to hash
	parse_headers: function(data){
		var result = new Object;
		var headers = data.split('\r\n\r\n');
		var upgradeHead = headers[1];
		
		headers = headers[0];
		if(headers) {
			headers = headers.split('\r\n');	
			var validHeader = true;
			headers.each(function(item, i){
				// i == 0 means the item equal 'GET /*** HTTP/1.1
				if(i !== 0) {
					var keyValue = item.split(': ');
					var key = keyValue[0];
					var value = keyValue[1];
					if(key) {
						if(key === 'Host' && keyValue[2]){
						    result[key] = (value) + ':' + keyValue[2];
						}
						else {
						    result[key] = value;
						}
					}
				}
				else {
					var args = item.split(' ');
					var method = args[0];
					var path = args[1];
					var prototype = args[2];
					if(path){
						//path is usefull in handshake
						result['path'] = path;
					}
					//must be GET /** HTTP/1.1
					validHeader = (method === 'GET' && 
								path[0]=== '/'&& 
								prototype ==="HTTP/1.1");
				}
			});	
			if(!validHeader) return null;
			return result;
		} 
		else return null;
	},
	//draft 75
	_shake75: function(headers){
		var result = ['HTTP/1.1 101 Web Socket Protocol Handshake\r\n',
          'Upgrade: WebSocket\r\n',
          'Connection: Upgrade\r\n',
          'WebSocket-Origin: ' + headers['Origin']  + '\r\n',
          'WebSocket-Location: ' + headers['Host'] + headers['path'] 
		];
		if(headers.hasOwnProperty('Protocol')){
			result.push('WebSocket-Protocol:' + headers['Protocol']);
		}
		return result.join('');
	},
	//draft 16
	_shake76: function(headers, upgradeHead){
		var result = ['HTTP/1.1 101 Web Socket Protocol Handshake\r\n',
          'Upgrade: WebSocket\r\n',
          'Connection: Upgrade\r\n',
          'Sec-WebSocket-Origin: ' + headers['Origin'] + '\r\n',
          'Sec-WebSocket-Location: ws://' + headers['Host'] + headers['path'] 
		];
		if(headers.hasOwnProperty('Sec-WebSocket-Protocol')){
			result.push('\r\nSec-WebSocket-Protocol:' + headers['Sec-WebSocket-Protocol']);
		}
      	var strkey1 = headers['Sec-WebSocket-Key1'],
          strkey2 = headers['Sec-WebSocket-Key2'],
          numkey1 = parseInt(strkey1.replace(/[^\d]/g, ''), 10),
          numkey2 = parseInt(strkey2.replace(/[^\d]/g, ''), 10),
          spaces1 = strkey1.replace(/[^\ ]/g, '').length,
          spaces2 = strkey2.replace(/[^\ ]/g, '').length;
      if (spaces1 == 0 || spaces2 == 0 ||
          numkey1 % spaces1 != 0 || numkey2 % spaces2 != 0) {
        	console.log('WebSocket Handshake contained an invalid key');
      } 
		else {
       	 var hash = Crypto.createHash('md5'),
       	     key1 = pack(parseInt(numkey1 / spaces1)),
       	     key2 = pack(parseInt(numkey2 / spaces2));
       	 hash.update(key1);
       	 hash.update(key2);
	console.log(typeof upgradeHead + ":" + upgradeHead);
       	 hash.update(upgradeHead);
         result.push('\r\n\r\n');
	var backHash = hash.digest('binary');
       	 result.push(backHash);
	console.log(backHash.length);
		}
		return result.join('');
	},
	response_shake: function(headers, upgradeHeader){
		//Sec-*************** exist in draft 76
		if(headers.hasOwnProperty('Sec-WebSocket-Key1') && headers.hasOwnProperty('Sec-WebSocket-Key2')){
			return this._shake76(headers, upgradeHeader.slice(0, 8));
		}
		else {
			return this._shake75(headers);
		}
	},
	recieve_socket_data: function(data){

	},
	send_socket_data: function(data){
		var msg = ['\x00',
			data,
			'\xFF'
		].join('');	
		
	}
});
server.listen(5216, '127.0.0.1');
